package org.gwt.grin.client.features;

import java.util.Map;

import org.gwt.grin.client.Director;
import org.gwt.grin.client.Feature;
import org.gwt.grin.client.util.BgImage;
import org.gwt.grin.client.util.Color;
import org.gwt.grin.client.util.Graphics;
import org.gwt.grin.client.util.Rectangle;

import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.BorderStyle;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.ui.Widget;

/**
 * 
 * @author Bill Foote (http://jovial.com)
 * @author ggeorg
 */
public class Box extends Feature {

	protected int x;
	protected int y;
	protected int width;
	protected int height;
	protected int outlineWidth;
	protected Color outlineColor;
	protected Color fillColor;
	protected BgImage bgImage;
	protected InterpolatedModel scalingModel = null;
	protected Rectangle scaledBounds = null;

	protected boolean isActivated;

	private Widget widget;

	public Box(String name) {
		super(name);
	}

	public void setup(int x, int y, int width, int height, int outlineWidth,
			Color outlineColor, Color fillColor, BgImage bgImage,
			InterpolatedModel scalingModel) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		this.outlineWidth = outlineWidth;
		this.outlineColor = outlineColor;
		this.fillColor = fillColor;
		this.bgImage = bgImage;
		this.scalingModel = scalingModel;
		if (scalingModel != null) {
			this.scaledBounds = new Rectangle();
		}
	}

	@Override
	protected Feature createClone(Map<Feature, Feature> clones) {
		if (!isSetup() || isActivated) {
			throw new IllegalStateException();
		}
		Box result = new Box(null);
		result.setup(x, y, width, height, outlineWidth, outlineColor,
				fillColor, bgImage, null);
		result.show = show; // FIXME
		if (scaledBounds != null) {
			result.scaledBounds = new Rectangle(scaledBounds);
		}
		return result;
	}

	@Override
	protected void initializeClone(Feature original,
			Map<Feature, Feature> clones) {
		super.initializeClone(original, clones);
		Box originalBox = (Box) original;
		scalingModel = (InterpolatedModel) Feature.clonedReference(
				originalBox.scalingModel, clones);
		widget = createWidget();
	}

	protected Widget createWidget() {
		return new BoxWidget();
	}

	@Override
	public int getX() {
		return x;
	}

	@Override
	public int getY() {
		return y;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	/**
	 * Resizes the box.
	 * <p>
	 * This should not be directly called by clients of the GRIN framework,
	 * unless it is done within a command body, or inside an implementation of
	 * {@link Director#nextFrame()}.
	 */
	public void resize(int x, int y, int width, int height) {
		this.x = x;
		this.y = y;
		this.width = width;
		this.height = height;
		markAsChanged();
	}

	/**
	 * Resizes the outline around the box.
	 * <p>
	 * This should not be directly called by clients of the GRIN framework,
	 * unless it is done within a command body, or inside an implementation of
	 * {@link Director#nextFrame()}.
	 */
	public void resizeOutline(int outlineWidth) {
		this.outlineWidth = outlineWidth;
		markAsChanged();
	}

	/**
	 * Changes the color of the box.
	 * <p>
	 * This should not be directly called by clients of the GRIN framework,
	 * unless it is done within a command body, or inside an implementation of
	 * {@link Director#nextFrame()}.
	 */
	public void changeBoxColor(Color c) {
		this.fillColor = c;
		markAsChanged();
	}

	/**
	 * Changes the color of the box outline.
	 * <p>
	 * This should not be directly called by clients of the GRIN framework,
	 * unless it is done within a command body, or inside an implementation of
	 * {@link Director#nextFrame()}.
	 */
	public void changeBoxOutlineColor(Color c) {
		this.outlineColor = c;
		markAsChanged();
	}

	@Override
	protected void initialize() {
		// do nothing
	}

	@Override
	protected void destroy() {
		if (widget == null) {
			return;
		}
		if (widget.isAttached()) {
			widget.removeFromParent();
		}
		widget = null;
	}

	@Override
	protected void setActivateMode(boolean mode) {
		isActivated = mode;
		if (mode) {
			// Setup initial values
			if (scalingModel != null) {
				this.scaledBounds.reshape(0, 0, 0, 0);
			}
			markAsChanged();
		} else {
			widget.removeFromParent();
		}
	}

	@Override
	protected int setSetupMode(boolean mode) {
		if (mode && widget == null) {
			widget = createWidget();
		}
		return 0;
	}

	@Override
	public boolean needsMoreSetup() {
		return false;
	}

	@Override
	public void nextFrame() {
		// do nothing
	}

	@Override
	public void paintFrame(Graphics gc) {
		int x1, y1, w, h;
		if (scalingModel != null
				&& scalingModel.scaleBounds(gc, x, y, width, height,
						scaledBounds)) {
			x1 = scaledBounds.x;
			y1 = scaledBounds.y;
			w = scaledBounds.width;
			if (w < 0) {
				w = -w;
				x1 -= w;
			}
			h = scaledBounds.height;
			if (h < 0) {
				h = -h;
				y1 -= h;
			}
			markAsChanged();
		} else {
			x1 = x;
			y1 = y;
			w = width;
			h = height;
		}

		if (!isActivated || !isChanged()) {
			return;
		}

		final Style widgetStyle = widget.getElement().getStyle();
		if (outlineColor != null && outlineWidth >= 0) {
			widgetStyle.setBorderWidth(outlineWidth, Unit.PX);
			widgetStyle.setBorderStyle(BorderStyle.SOLID);
			widgetStyle.setBorderColor(outlineColor.toString());
		}
		if (fillColor != null) {
			widgetStyle.setBackgroundColor(fillColor.toString());
		}
		if (bgImage != null) {
			bgImage.applyTo(widget);
		}

		gc.setBounds(widget, x1, y1, w, h);
	}

	private class BoxWidget extends Widget {
		public BoxWidget() {
			setElement(Document.get().createDivElement());
			setStyleName("grin-Box");
		}
	}
}
